/*
    日志器模块
    将格式化模块、日志等级、落地类、日志消息模块结合起来，用一个类来实现日志的创建，格式化，到落地这整个过程
    日志器中包含限制等级，低于限制等级的日志不会被打印
    日志器中的各种等级接口，用的是输出不同等级的日志---内部首先判断日志等级，对不定参正文进行解析成字符串，然后进行格式化还是落地
    抽象出来的日志器用来派生同步日志器和异步日志器

    但是直接使用日志器模块，会导致使用成本增高，学习成本升高
    因为建造一个日志器需要用到多个组件：等级、日志器名称、格式化对象、落地器对象等等
    所以我们可以使用建造者模式，来对创建日志器的过程进行建模
*/

#pragma once
#include "util.hpp"
#include "level.hpp"
#include "format.hpp"
#include "sink.hpp"
#include "message.hpp"
#include "looper.hpp"
#include <atomic>
#include <mutex>
#include <cstdarg>
#include <unordered_map>

namespace logging
{
    class logger
    {
    public:
        using s_ptr = std::shared_ptr<logger>;
    public:
        logger(const std::string &logger_name, logLevel::value level, formatter::s_ptr &formatter, 
                std::vector<sink::s_ptr> &sinks)
            :_limits_rank(level), _formatter(formatter), _sinks(sinks.begin(), sinks.end()), _logger_name(logger_name)
        {}

        const std::string &name() { return _logger_name; }

        void debug(const std::string &file, const size_t line, const std::string &fmt, .../*正文部分*/)
        {
            // 日志等级输出接口，用于将数据格式化为一个日志消息对象，然后通过日志落地模块，将日志器输出到不同位置
            // 1.判断日志等级与限制等级
            if(logLevel::value::DEBUG < _limits_rank) return;

            // 2. 使用fmt格式，对不定参进行解析
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if(n == -1) {
                std::cout << "vasprintf filed!!!";
                free(res);
                return;
            }
            va_end(ap);

            // 3.构建一个日志消息对象, 并通过具体落地方式和方向，进行落地
            serialize(logLevel::value::DEBUG, file, line, res);
        }
        void info(const std::string &file, const size_t line, const std::string &fmt, ...)
        {
            if(logLevel::value::INFO < _limits_rank) return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if(n == -1) {
                std::cout << "vasprintf filed!!!";
                free(res);
                return;
            }
            va_end(ap);
            serialize(logLevel::value::INFO, file, line, res);
        }
        void warnning(const std::string &file, const size_t line, const std::string &fmt, ...)
        {
            if(logLevel::value::WARNNING < _limits_rank) return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if(n == -1) {
                std::cout << "vasprintf filed!!!";
                free(res);
                return;
            }
            va_end(ap);
            serialize(logLevel::value::WARNNING, file, line, res);
        }
        void error(const std::string &file, const size_t line, const std::string &fmt, ...)
        {
            if(logLevel::value::ERROR < _limits_rank) return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if(n == -1) {
                std::cout << "vasprintf filed!!!";
                free(res);
                return;
            }
            va_end(ap);
            serialize(logLevel::value::ERROR, file, line, res);
        }
        void fatal(const std::string &file, const size_t line, const std::string &fmt, ...)
        {
            if(logLevel::value::FATAL < _limits_rank) return;
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int n = vasprintf(&res, fmt.c_str(), ap);
            if(n == -1) {
                std::cout << "vasprintf filed!!!";
                free(res);
                return;
            }
            va_end(ap);
            serialize(logLevel::value::FATAL, file, line, res);
        }
    protected:
        void serialize(logLevel::value level, const std::string &file, const size_t line, const char *fmt)
        {
            // 构建日志消息对象
            logMessage msg(level, file, line, _logger_name, fmt);
            // 将日志消息对象格式化为一个格式化字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 通过落地方式，进行落地
            logSink(ss.str().c_str(), ss.str().size());
        }
        virtual void logSink(const char *data, const size_t len) = 0;
    protected:
        // 日志器输出等级，日志输出前都必须访问该限制等级，判断是否可以输出，所以为了避免多线程导致线程安全，这里使用原子类型
        std::atomic<logLevel::value> _limits_rank;
        formatter::s_ptr _formatter;     // 日志器格式
        std::vector<sink::s_ptr> _sinks; // 日志器落地对象，因为一个日志可能又多个落地方向,所以这里使用数组，包含多个落地对象
        std::string _logger_name;        // 日志器名称——日志器的唯一标识符
        std::mutex _mutex;               // 互斥锁，保证在多线程输出时的线程安全
    };

    // 同步日志器
    class syncLogger : public logger
    {
    public:
        syncLogger(const std::string &logger_name, const logLevel::value level, formatter::s_ptr &formatter, 
                    std::vector<sink::s_ptr> &sink)
            :logger(logger_name, level, formatter, sink)
        {}
    protected:
        void logSink(const char *data, const size_t len) override
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(_sinks.empty()) return;
            for(auto sink : _sinks)
                sink->logSink(data, len);
        }
    };

    // 异步日志器
    class asyncLogger : public logger
    {
    public:
        asyncLogger(const std::string &logger_name, const logLevel::value level, formatter::s_ptr &formatter, 
                    std::vector<sink::s_ptr> &sink, asyncType looper_type)
            :logger(logger_name, level, formatter, sink)
        {
            _looper = std::make_shared<asyncLooper>(std::bind(&asyncLogger::realSink, this, std::placeholders::_1), looper_type);
        }
        // 对于异步日志器来说，只需要将消息写入到缓冲区中即可
        void logSink(const char *data, const size_t len) { _looper->push(data, len); }
        void realSink(buffer &buf)
        {
            if(_sinks.empty()) return;
            for(auto sink : _sinks) {
                sink->logSink(buf.begin(), buf.readAbleSize());
            }
        }
    private:
        asyncLooper::s_ptr _looper;
    };


    // 用户直接使用上面的日志器接口去创建日志器，会导致用户需要非常清楚各种接口，提高了用户的使用复杂度和成本
        // 创建一个同步日志器
        //std::string logger_name = "sync-logger";
        //logging::logLevel::value limit = logging::logLevel::value::WARNNING;
        //logging::formatter::s_ptr formatter(new logging::formatter());
        //logging::sink::s_ptr ssink = logging::sinkFactory::create<logging::stdoutSink>();
        //logging::sink::s_ptr fsink = logging::sinkFactory::create<logging::fileSink>("./test-for-logger/testlog.log");
        //logging::sink::s_ptr rfsink = logging::sinkFactory::create<logging::rollSinkBySize>("./test-for-logger/sync-logger-", 1024 * 1024);
        //std::vector<logging::sink::s_ptr> sinks{ssink, fsink, rfsink};
        //logging::syncLogger::s_ptr sync_logger(new logging::syncLogger(logger_name, limit, formatter, sinks));
    // 所以，我们这里可以使用建造者设计模式来简化用户创建日志器的过程
    // 建造者模式，先设计出所有的零部件，再通过对零部件的组装，建造出一个具体对象
    // 设计思想：
    //      1、设计出一个抽象的建造者类
    //      2、派生出具体的建造者类，分为局部建造者类和全局建造者类
    //          之所以，不派生出同步建造者类和异步建造者类，是因为我们可以通过增加日志器类型参数来用一个建造者类实现

    enum class loggerType
    {
        SYNC_LOGGER,
        ASYNC_LOGGER
    };

    class loggerBuilder
    {
    public:
        using s_ptr = std::shared_ptr<loggerBuilder>;
        loggerBuilder()
            :_limits_rank(logLevel::value::DEBUG)
            ,_logger_type(loggerType::SYNC_LOGGER)
            ,_async_type(asyncType::ASYNC_SAFE)
        {}
    public:
        void buildEnableUnsafe() { _async_type = asyncType::ASYNC_UNSAFE; }
        void buildLimitRank(logLevel::value level) { _limits_rank = level; }
        void buildLoggerName(const std::string &loggername) { _logger_name = loggername; }
        void buildLoggerType(const loggerType &loggertype) { _logger_type = loggertype; }
        void buildFormatter(const std::string &pattern = "[%d{%Y%m%d-%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n") { _formatter = std::make_shared<formatter>(pattern); }
        template <typename sinkType, typename ...Args>
        void buildSinks(Args &&...args) 
        {
            sink::s_ptr sink = std::make_shared<sinkType>(std::forward<Args>(args)...);
            _sinks.emplace_back(sink);
        }
        virtual logger::s_ptr build() = 0;
    protected:
        asyncType _async_type;
        loggerType _logger_type;
        logLevel::value _limits_rank;
        formatter::s_ptr _formatter;     // 日志器格式
        std::vector<sink::s_ptr> _sinks; // 日志器落地对象，因为一个日志可能又多个落地方向,所以这里使用数组，包含多个落地对象
        std::string _logger_name;        // 日志器名称——日志器的唯一标识符
    };

    // 局部日志器建造者
    class localLoggerBuilder : public loggerBuilder
    {
    public:
        logger::s_ptr build() override
        {
            // 日志器必须得有名称
            assert(!_logger_name.empty());
            if(_formatter.get() == nullptr)  _formatter = std::make_shared<formatter>();
            if(_sinks.empty())  buildSinks<stdoutSink>();
            if(_logger_type == loggerType::ASYNC_LOGGER) 
                return std::make_shared<asyncLogger>(_logger_name, _limits_rank, _formatter, _sinks, _async_type);
            return std::make_shared<syncLogger>(_logger_name, _limits_rank, _formatter, _sinks);
        }
    };

    // 单例日志器管理器
    class loggerManager
    {
    public:
        // 获取单例接口
        static loggerManager& getInstance()
        {
            // c++11之后，创建静态局部变量是线程安全的
            static loggerManager eton;
            return eton;
        }

    public:
        void addLogger(logger::s_ptr &logger)
        {
            if(hasLogger(logger->name())) return;
            std::unique_lock<std::mutex> lock(_mutex);
            _loggers.emplace(logger->name(), logger);
        }

        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end()) return false;
            return true;
        }

        logger::s_ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _loggers.find(name);
            if(it == _loggers.end()) return logger::s_ptr();
            return it->second;
        }

        logger::s_ptr rootLogger() { return _root_logger; }
    private:
        loggerManager()
        {
            std::shared_ptr<loggerBuilder> builder = std::make_shared<localLoggerBuilder>();
            builder->buildLoggerName("root");
            _root_logger = builder->build();
            _loggers.emplace("root", _root_logger);
        }
        loggerManager(const loggerManager &manager) = delete;
        loggerManager operator=(const loggerManager &manager) = delete;
    private:
        logger::s_ptr _root_logger;
        std::unordered_map<std::string, logger::s_ptr> _loggers;
        std::mutex _mutex;
    };

    // 全局日志器建造者
    // 简便用户操作，全局日志器建造者建造出的日志器，会自动添加到日志器管理者内部
    class globalLoggerBuilder : public loggerBuilder
    {
    public:
        logger::s_ptr build() override
        {
            // 日志器必须得有名称
            assert(!_logger_name.empty());
            if(_formatter.get() == nullptr)  _formatter = std::make_shared<formatter>();
            if(_sinks.empty())  buildSinks<stdoutSink>();
            logger::s_ptr ret;
            if(_logger_type == loggerType::ASYNC_LOGGER) 
                ret = std::make_shared<asyncLogger>(_logger_name, _limits_rank, _formatter, _sinks, _async_type);
            else
                ret = std::make_shared<syncLogger>(_logger_name, _limits_rank, _formatter, _sinks);
            loggerManager::getInstance().addLogger(ret);
            return ret;
        }
    };
}